package org.eclipse.moquette.parser.netty;

import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.AttributeMap;
import org.eclipse.moquette.proto.messages.AbstractMessage;

import java.util.List;

/**
 * Created by alex on 15-5-19.
 */
public abstract class DemuxDecoder {

    abstract void decode(AttributeMap ctx, ByteBuf in, List<Object> out) throws Exception;

    /**
     * 解码头两个字节的MQTT协议包
     *
     * Decodes the first 2 bytes of the MQTT packet.
     * 第一个字节包含操作码和flag
     * The first byte contain the packet operation code and the flags,
     * 第二个字节包含整个包的长度
     * the second byte and more contains the overall packet length.
     */
    protected boolean decodeCommonHeader(AbstractMessage message, ByteBuf in) {
        return genericDecodeCommonHeader(message, null, in);
    }

    /**
     * 解码静态头部
     * @param message 消息
     * @param expectedFlags 期望的flag 字节 ，eg expectedFlags 是 0000 ， 但是解析的的后四位是0010 那么就会抛出 CorruptedFrameException
     * @param in ByteBuf
     * Do the same as the {@link #decodeCommonHeader} but having a strong validation on the flags values
     */
    protected boolean decodeCommonHeader(AbstractMessage message, int expectedFlags, ByteBuf in) {
        return genericDecodeCommonHeader(message, expectedFlags, in);
    }

    private boolean genericDecodeCommonHeader(AbstractMessage message, Integer expectedFlagsOpt, ByteBuf in) {
        //common decoding part
        if (in.readableBytes() < 2){
            return false;
        }
        byte h1 = in.readByte();
        byte messageType = (byte) ((h1 & 0x00f0) >> 4); //前4为 & 1111

        byte flags = (byte) (h1 & 0x0f); //后4位 & 1111
        if (expectedFlagsOpt != null){
            int expectedFlags = expectedFlagsOpt;
            if ((byte) expectedFlags != flags) { //验证后四位 是否符合消息协议的格式
                String hexExpected = Integer.toHexString(expectedFlags);
                String hexReceived = Integer.toHexString(flags);
                throw new CorruptedFrameException(String.format("Received a message with fixed header flags (%s) != expected (%s)", hexReceived, hexExpected));
            }
        }
        //得到DUP flag
        boolean dupFlag = ((byte) ((h1 & 0x0008) >> 3) == 1);

        //得到Qos level
        byte qosLevel = (byte) ((h1 & 0x0006) >> 1);
        // 得到RETAIN flag
        boolean retainFlag = ((byte) (h1 & 0x0001) == 1);

        int remainingLength = Utils.decodeRemainingLength(in);
        if (remainingLength == -1) {
            return false;
        }

        //填充消息头对象
        message.setMessageType(messageType);
        message.setDupFlag(dupFlag);
        message.setQos(AbstractMessage.QOSType.values()[qosLevel]);
        message.setRetainFlag(retainFlag);
        message.setRemainingLength(remainingLength);
        return true;
    }
}
